﻿// PortableMediator.Test
// (c) Lance Wynn - http://PortableMediator.codeplex.com/
// License: MIT (http://www.opensource.org/licenses/mit-license.php)

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace PortableMediator.Test
{
    [TestClass]
    public class Mediator_Will_Publish_Messages
    {
        [TestMethod]
        public async Task Correctly_To_Void_Method_Subscriber()
        {
            IMediator mediator = new Mediator();
            var testSubscriber = new TestModels.TestSubscriber();
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkVoid");
            await mediator.PublishMessage("test", "Param");
            Assert.AreEqual(1, testSubscriber.MessageSinkVoidCalledCount);
        }

        [TestMethod]
        public async Task Correctly_To_Async_Task_Method_Subscriber()
        {
            IMediator mediator = new Mediator();
            var testSubscriber = new TestModels.TestSubscriber();
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTask");
            await mediator.PublishMessage("test", "Param");
            Assert.AreEqual(1, testSubscriber.MessageSinkAsyncTaskCalledCount);
        }

        [TestMethod]
        public async Task Correctly_To_Async_TwoWay_Method_Subscriber_With_Callback()
        {
            IMediator mediator = new Mediator();
            var testSubscriber = new TestModels.TestSubscriber();
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");

            bool callbackInvokedCorrectly = false;

            await mediator.PublishMessage("test", "Param", (string param) => { callbackInvokedCorrectly = param == "Param"; });
            Assert.AreEqual(1, testSubscriber.MessageSinkAsyncTaskOfStringCalledCount);
            Assert.IsTrue(callbackInvokedCorrectly);
        }

        [TestMethod]
        public async Task Correctly_To_Async_TwoWay_Method_Subscriber_DirectResult()
        {
            IMediator mediator = new Mediator();
            var testSubscriber = new TestModels.TestSubscriber();
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");

            var results = await mediator.PublishMessageWithReturnResult<string, string>("test", "Param");

            Assert.AreEqual(5, results.Count()); ////Each subscriber should be called once.
        }

        [TestMethod]
        public async Task Correctly_To_TwoWay_Method_Subscriber_DirectResult()
        {
            IMediator mediator = new Mediator();
            var testSubscriber = new TestModels.TestSubscriber();
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");

            var results = await mediator.PublishMessageWithReturnResult<string, string>("test", "Param");

            Assert.AreEqual(5, results.Count()); ////Each subscriber should be called once.
        }

        [TestMethod]
        public async Task Correctly_To_Async_TwoWay_Method_Subscriber_DirectResult_With_Invalid_Result_Type()
        {
            IMediator mediator = new Mediator();
            var testSubscriber = new TestModels.TestSubscriber();
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkAsyncTaskOfString");

            var results = await mediator.PublishMessageWithReturnResult<string, int>("test", "Param");
            Assert.AreEqual(5, testSubscriber.MessageSinkAsyncTaskOfStringCalledCount); ////Each subscriber should be called once.
            Assert.AreEqual(0, results.Count()); //None of the subscribers should return a result
        }

        [TestMethod]
        public async Task Correctly_To_TwoWay_Method_Subscriber_DirectResult_With_Invalid_Result_Type()
        {
            IMediator mediator = new Mediator();
            var testSubscriber = new TestModels.TestSubscriber();
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");

            var results = await mediator.PublishMessageWithReturnResult<string, int>("test", "Param");

            Assert.AreEqual(5, testSubscriber.MessageSinkStringCalledCount); ////Each subscriber should be called once.
            Assert.AreEqual(0, results.Count()); ////none of the subscribers should return a value to the result set.
        }


        [TestMethod]
        public async Task Correctly_Publish_Messages_To_Attribute_decorated_Method()
        {
            IMediator mediator = new Mediator();

            var testSubscriber = new TestModels.TestSubscriber();

            await mediator.RegisterSubscriber(testSubscriber);

            await mediator.PublishMessage("test", "Param");

            Assert.AreEqual(1, testSubscriber.MessageAttributeSinkCalledCount);
        }

        [TestMethod]
        public async Task Correctly_Publish_Messages_To_Attribute_decorated_Property()
        {
            IMediator mediator = new Mediator();

            var testSubscriber = new TestModels.TestSubscriber();

            await mediator.RegisterSubscriber(testSubscriber);

            await mediator.PublishMessage("testProp", "Param");

            Assert.AreEqual("Param", testSubscriber.MessageSinkStringProperty);
        }

        [TestMethod]
        public async Task Correctly_Publish_Messages_To_Manually_Registered_Property()
        {
            IMediator mediator = new Mediator();

            var testSubscriber = new TestModels.TestSubscriber();

            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkStringProperty");

            await mediator.PublishMessage("test", "Param");

            Assert.AreEqual("Param", testSubscriber.MessageSinkStringProperty);
        }

        [TestMethod]
        public async Task Correctly_Publish_Messages_To_Anonymous_Actions()
        {
            IMediator mediator = new Mediator();

            bool messageReceivedInAnonMethod = false;

            await mediator.RegisterSubscriber("test", (string param) => { messageReceivedInAnonMethod = param == "Param"; });

            await mediator.PublishMessage("test", "Param");

            Assert.IsTrue(messageReceivedInAnonMethod);
        }

        [TestMethod]
        public async Task Correctly_Publish_Messages_To_Anonymous_Functions_And_Receive_Results()
        {
            IMediator mediator = new Mediator();

            await mediator.RegisterSubscriber("test", this, (string param) => { return param; });

            var results = await mediator.PublishMessageWithReturnResult<string, string>("test", "Param");

            Assert.AreEqual(1, results.Count());
        }

        [TestMethod]
        public async Task Correctly_Publish_Messages_To_Methods_With_Matching_Interface_Param()
        {
            IMediator mediator = new Mediator();

            var testSubscriber = new TestModels.TestSubscriber();

            await mediator.RegisterSubscriber("testIF", testSubscriber, "BaseMessageSinkIFParam");

            await mediator.RegisterSubscriber("testIF", testSubscriber, "BaseMessageSinkBaseParam");

            await mediator.PublishMessage("testIF", testSubscriber);

            Assert.AreEqual(1, testSubscriber.BaseMessageSinkIFParamCalledCount);
            Assert.AreEqual(1, testSubscriber.BaseMessageSinkBaseParamCalledCount);
        }

        [TestMethod]
        public async Task To_The_Message_Published_Token_When_A_Message_Is_Published()
        {
            IMediator mediator = new Mediator();
            var testSubscriber = new TestModels.TestSubscriber();
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");

            bool messagePublished = false;
            await mediator.RegisterSubscriber(PortableMediator.MediatorTokens.StandardMediatorTokens.MessagePublished,
                (MessagePublishedArgs args) =>
                {
                    messagePublished = true;
                    Assert.AreEqual(5, args.SubscriberCount);
                    Assert.AreEqual("Param", args.Parameter);
                    Assert.AreEqual("test", args.Token);
                });

            var results = await mediator.PublishMessageWithReturnResult<string, string>("test", "Param");
            Assert.IsTrue(messagePublished);
        }

        [TestMethod]
        public async Task To_The_Token_Registered_Token_When_A_Message_Subscriber_Is_Registered()
        {
            IMediator mediator = new Mediator();
            var testSubscriber = new TestModels.TestSubscriber();

            bool messagePublishedReceived = false; ///We should never receive this

            await mediator.RegisterSubscriber(PortableMediator.MediatorTokens.StandardMediatorTokens.MessagePublished,
            (MessagePublishedArgs args) =>
            {
                messagePublishedReceived = true;
            });

            int tokenRegisteredMessageReceivedCount = 0; ///we should receive this 1 time for each registration
            await mediator.RegisterSubscriber(PortableMediator.MediatorTokens.StandardMediatorTokens.TokenRegistered,
            (object token) =>
            {
                tokenRegisteredMessageReceivedCount++;
            });


            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");
            await mediator.RegisterSubscriber("test", testSubscriber, "MessageSinkString");

            Assert.IsFalse(messagePublishedReceived);
            Assert.AreEqual(5, tokenRegisteredMessageReceivedCount);
        }
    }
}
